/*
 * Copyright 2004-2024 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (https://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.expression.function;

import org.h2.engine.SessionLocal;
import org.h2.expression.Expression;
import org.h2.expression.TypedValueExpression;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueVarchar;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;
import java.util.regex.Pattern;


/**
 * A CONCAT or CONCAT_WS function.
 */
public final class ChatgptFunction extends FunctionN {

    /**
     * CONCAT() (non-standard).
     */
    public static final int CHATGPT = 0;

    /**
     * CONCAT_WS() (non-standard).
     */
    public static final int CHATGPT_WS = CHATGPT + 1;

    private static final String[] NAMES = { //
            "CHATGPT", "CHATGPT_WS" //
    };

    private final int function;

    public ChatgptFunction(int function) {
        this(function, new Expression[4]);
    }

    public ChatgptFunction(int function, Expression... args) {
        super(args);
        this.function = function;
    }


    private static String decodeUnicode(String str) {
        // 去除前缀U&
        if (!str.startsWith("U&")) {
            return str.trim();
        }
        str = str.substring(2);
        // 去除单引号
        if (str.startsWith("'") && str.endsWith("'")) {
            str = str.substring(1, str.length() - 1);
        }
        str = str.replace("\\", "\\u");
        // 分割字符串为 Unicode 码点
        // 解析Unicode转义序列
        StringBuilder sb = new StringBuilder();
        int i = 0;
        while (i < str.length()) {
            if (str.startsWith("\\u\\un", i)) {
                sb.append("\n");
                i += 5;
            } else if (str.startsWith("\\u\\ur", i)) {
                sb.append("\r");
                i += 5;
            } else if (str.startsWith("\\u", i)) {
                String hex = str.substring(i + 2, i + 6);
                sb.append((char) Integer.parseInt(hex, 16));
                i += 6;
            } else {
                // 如果不是Unicode转义序列，则直接添加字符
                sb.append(str.charAt(i));
                i++;
            }
        }
        return sb.toString().trim();
    }

    /**
     * 从当前jar包复制python到本地执行
     *
     * @param dir  文件夹
     * @param file py文件
     * @return 返回文件
     */
    public static String copyPythonFile(String dir, String file) {
        String pyName = dir + "/" + file + ".py";
        File pyFile = new File(pyName);
        System.out.println("pyName=" + pyName);
        if (pyFile.exists()) {
            return pyName;
        }
        try {
            byte[] data = Utils.getResource("/org/h2/server/web/res/llm/" + file + ".py");
            if (data == null) {
                System.out.println("[copyPythonFile] data=null");
                return "python file  data=null";
            }
            boolean bNew = pyFile.createNewFile();
            if (!bNew) {
                return "权限不够,无法创建临时文件";
            }
            try (FileOutputStream fos = new FileOutputStream(pyFile)) {
                fos.write(data);
            }
        } catch (Exception e) {
            System.out.println("[copyPythonFile] error:" + e.getMessage());
        }
        return pyName;
    }

    /**
     * md5算法
     *
     * @param originalString 原始字符串
     * @return 返回md5
     */
    public static String calculateMD5(String originalString) {
        try {
            // 创建MD5加密实例
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 执行加密操作
            byte[] messageDigest = md.digest(originalString.getBytes());
            // 将得到的散列值转换为十六进制
            StringBuilder sb = new StringBuilder();
            for (byte b : messageDigest) {
                sb.append(String.format("%02x", b));
            }
            // 返回MD5散列值
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("MD5加密算法不可用", e);
        }
    }

    /**
     * 获取 源码
     *
     * @param fileName 文件名
     * @return 返回内容
     * @throws IOException
     */
    public static String getSource(String fileName) throws IOException {
        byte[] data = Utils.getResource("/org/h2/server/web/res/llm/" + fileName + ".py");
        if (data == null) {
            System.out.println("[copyPythonFile] data=null");
            return "python file  data=null";
        }
        return new String(data, StandardCharsets.UTF_8);
    }

    /**
     * 获取参数值
     *
     * @param session 会话
     * @param args    参数
     * @param index   序号
     * @return 值
     */
    public static String getValue(SessionLocal session, Expression[] args, int index) {
        return index < args.length ? args[index].getValue(session).getString() : null;
    }

    /**
     * 运行python代码
     *
     * @param args    参数
     * @param session 会话
     * @return 返回执行的值
     */
    public static String runPython(String file, Expression[] args, SessionLocal session, int startIndex) {
//        System.out.println("[runPython] start");
        //System.out.println("[ChatgptFunction]name = " + session.getDatabase().getName());
        ///data/bzmwork/softrobot/llm/h2db/llm.data
//        String path = session.getDatabase().getDatabasePath();
//        System.out.println("[runPython]path=" + path);
//        if (path == null) {
//            return "数据库的路径错误";
//        }
//        String pmd5 = ChatgptFunction.calculateMD5(path);
//        int start = path.lastIndexOf("/");
//        String dbDir = path.substring(0, start);
//
//        File tmpDir = new File(System.getProperty("java.io.tmpdir"));
//        File myTempDir = new File(tmpDir.getAbsolutePath(), pmd5);
//        // 创建目录
//        if (!myTempDir.exists()) {
//            if (!myTempDir.mkdir()) {
//                System.err.println("create error: " + myTempDir.getAbsolutePath());
//            }
//        }
//        String dir = myTempDir.getAbsolutePath();
//        ChatgptFunction.copyFile(dbDir + "/.env", dir + "/.env");
//        //保证apibase和apibase存在
//        String[] bases = {"ApiBase", "ApiTools", "ApiVctSvr", "ApiVector", "ApiTol", "ApiRft", "ApiSft", "ApiGrp", "ApiAct", "ApiSqlVector", "ApiModels", "rank_bm25"};
//        for (String name : bases) {
//            ChatgptFunction.copyPythonFile(dir, name);
//        }
        return ChatgptFunction.runPython_raw("", file, args, session, startIndex);
    }

    public static void copyFile(String sourcePath, String destinationPath) {
        // 确定目标文件是否存在
        java.io.File destinationFile = new java.io.File(destinationPath);
        if (destinationFile.exists()) {
            // 如果文件存在，则删除它
            //destinationFile.delete();
            return;
        }

        // 复制文件
        try (InputStream inputStream = new FileInputStream(sourcePath);
             OutputStream outputStream = new FileOutputStream(destinationPath)) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }
        } catch (Exception e) {
            System.out.println("[copyFile]-error:" + e.getMessage());
        }
    }

    /**
     * 在graalVM 里面找不到资源
     *
     * @param file       文件
     * @param args       参数
     * @param session    会话
     * @param startIndex 参数开始
     * @return 值
     */
    public static String runPython_tmp(String file, Expression[] args, SessionLocal session, int startIndex) {
        String path = session.getDatabase().getDatabasePath();
        if (path == null) {
            return "数据库的路径错误";
        }
        int start = path.lastIndexOf("/");
        String dbDir = path.substring(0, start);
        return ChatgptFunction.runPython_raw(dbDir, file, args, session, startIndex);
    }

    /**
     * 运行python代码
     *
     * @param args    参数
     * @param session 会话
     * @return 返回执行的值
     */
    public static String runPython_raw_1(String dbDir, String file, Expression[] args, SessionLocal session, int startIndex) {
        //System.out.println("[runPython_graalvm]");
        ArrayList<String> lsParam = new ArrayList<>();
        lsParam.add("python3");
        String pyName = ChatgptFunction.copyPythonFile(dbDir, file);

        lsParam.add(pyName);
        for (int i = startIndex; i < args.length; i++) {
            String param = args[i].getValue(session).toString().trim();
            System.out.println("[runPython]old-param=" + param);
            param = ChatgptFunction.decodeUnicode(param);
            System.out.println("[runPython]decode-param=" + param);
            lsParam.add(param);
        }
        StringBuilder ret = new StringBuilder();
        System.out.println("[runPython] runPython python =" + pyName);
        ProcessBuilder pb = new ProcessBuilder(lsParam);
        Process p;
        try {
            p = pb.start();
            BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                if (!ret.isEmpty()) {
                    ret.append("\n");
                }
                ret.append(line);
            }
        } catch (Exception e) {
            System.out.println("[runPython_raw] Exception=" + e);
        }
        //System.out.println("[runPython] ret=" + ret);
        return ret.toString();
    }

    @Override
    public Value getValue(SessionLocal session) {
        if (args.length < 1) {
            return ValueVarchar.get("至少需要1个参数", session);
        }
        String file = args[0].getValue(session).toString().trim();
        file = file.replace("'", "");
        String path = session.getDatabase().getDatabasePath();
        if (path == null) {
            return ValueVarchar.get("数据库的路径错误", session);
        }
        int start = path.lastIndexOf("/");
        String dbDir = path.substring(0, start);
        String ret = ChatgptFunction.runPython_raw(dbDir, file, args, session, 1);
        return ValueVarchar.get(ret, session);
    }


    @Override
    public Expression optimize(SessionLocal session) {
        boolean allConst = optimizeArguments(session, true);
        int i = 0;
        long extra = 0L;
        if (function == CHATGPT_WS) {
            i = 1;
            extra = getPrecision(0);
        }
        long precision = 0L;
        int l = args.length;
        boolean f = false;
        for (; i < l; i++) {
            if (args[i].isNullConstant()) {
                continue;
            }
            precision = DataType.addPrecision(precision, getPrecision(i));
            if (extra != 0L && f) {
                precision = DataType.addPrecision(precision, extra);
            }
            f = true;
        }
        type = TypeInfo.getTypeInfo(Value.VARCHAR, precision, 0, null);
        if (allConst) {
            return TypedValueExpression.getTypedIfNull(getValue(session), type);
        }
        return this;
    }

    private long getPrecision(int i) {
        TypeInfo t = args[i].getType();
        int valueType = t.getValueType();
        if (valueType == Value.NULL) {
            return 0L;
        } else if (DataType.isCharacterStringType(valueType)) {
            return t.getPrecision();
        } else {
            return Long.MAX_VALUE;
        }
    }

    @Override
    public String getName() {
        return NAMES[function];
    }

    /**
     * 改进型
     *
     * @param val1 向量1
     * @param val2 向量2
     * @return 值
     */
    public static double calculateCosineSimilarity_adjust(String val1, String val2) {
        String[] stringArray1 = val1.split(",");
        double[] vector1 = new double[stringArray1.length];
        for (int i = 0; i < stringArray1.length; i++) {
            vector1[i] = Double.parseDouble(stringArray1[i]);
        }
        String[] stringArray2 = val2.split(",");
        double[] vector2 = new double[stringArray2.length];
        for (int i = 0; i < stringArray2.length; i++) {
            vector2[i] = Double.parseDouble(stringArray2[i]);
        }
        if (vector1.length != vector2.length) {
            return 0;
        }
        double sum1 = 0.0;
        double sum2 = 0.0;
        double sum1Sq = 0.0;
        double sum2Sq = 0.0;
        double pSum = 0.0;
        for (int i = 0; i < vector1.length; i++) {
            double x = vector1[i];
            double y = vector2[i];
            sum1 += x;
            sum2 += y;
            sum1Sq += Math.pow(x, 2);
            sum2Sq += Math.pow(y, 2);
            pSum += x * y;
        }
        double num = pSum - (sum1 * sum2 / vector1.length);
        double den = Math.sqrt((sum1Sq - Math.pow(sum1, 2) / vector1.length)
                * (sum2Sq - Math.pow(sum2, 2) / vector1.length));
        if (den == 0) {
            return 0.0;
        }
        return num / den;
    }

    /**
     * 计算两个向量之间的余弦相似度
     *
     * @param val1 第一个向量
     * @param val2 第二个向量
     * @return 余弦相似度
     */
    public static double calculateCosineSimilarity(String val1, String val2) {
        //System.out.println("val1=" + val1 + ";val2=" + val2);
        String[] stringArray1 = val1.split(",");
        double[] vector1 = new double[stringArray1.length];
        for (int i = 0; i < stringArray1.length; i++) {
            vector1[i] = Double.parseDouble(stringArray1[i]);
        }
        String[] stringArray2 = val2.split(",");
        double[] vector2 = new double[stringArray2.length];
        for (int i = 0; i < stringArray2.length; i++) {
            vector2[i] = Double.parseDouble(stringArray2[i]);
        }
        if (vector1.length != vector2.length) {
            return 0;
        }

        double dotProduct = 0.0;
        double normVector1 = 0.0;
        double normVector2 = 0.0;
        for (int i = 0; i < vector1.length; i++) {
            dotProduct += vector1[i] * vector2[i];
            normVector1 += Math.pow(vector1[i], 2);
            normVector2 += Math.pow(vector2[i], 2);
        }
        normVector1 = Math.sqrt(normVector1);
        normVector2 = Math.sqrt(normVector2);
        // Avoid division by zero
        if (normVector1 == 0 || normVector2 == 0) {
            return 0;
        }
        //System.out.println("dotProduct=" + dotProduct + ";normVector2=" + normVector2);
        return dotProduct / (normVector1 * normVector2);
    }

    // 定义 JSON 中需要转义的特殊字符正则表达式
    public static boolean containsSpecialOrControlCharacters(String input) {
        if (input == null || input.isEmpty()) {
            return false;
        }
        for (char c : input.toCharArray()) {
            // 检查是否为 JSON 特殊字符或控制字符 (ASCII 0-31)
            if (c == '\"' || c == '\\' || c == '\n' || c == '\r' || c == '\t' || c == '\b' || c == '\f' || c < 32) {
                return true;
            }
        }
        return false;
    }

    public static String runPython_raw(String dbDir, String file, Expression[] args, SessionLocal session, int startIndex) {
        StringBuilder input = new StringBuilder();
        input.append("{\"name\":\"").append(file).append("\",");
        int index = 0;
        for (int i = startIndex; i < args.length; i++) {
            String param = args[i].getValue(session).toString().trim();
            //System.out.println("[runPython]old-param=" + param);
            param = ChatgptFunction.decodeUnicode(param);
            if (index > 0) {
                input.append(",");
            }
            if (param.startsWith("'")) {
                param = param.substring(1, param.length() - 1);
            }
            // 转成base64
            if (containsSpecialOrControlCharacters(param)) {
                param = Base64.getEncoder().encodeToString(param.getBytes(StandardCharsets.UTF_8));
            }
            input.append("\"param").append(index).append("\":\"").append(param).append("\"");
            index++;
        }
        for (; index < 6; index++) {
            if (index > 0) {
                input.append(",");
            }
            input.append("\"param").append(index).append("\":\"\"");
        }
        input.append("}");
        System.out.println("[runPython_raw]" + input);
        try {
            URL url = new URL("http://127.0.0.1:61818/llm");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            // 设置请求头，如Content-Type
            conn.setRequestProperty("Content-Type", "application/json");
            // 发送请求参数
            byte[] outputInBytes = input.toString().getBytes(StandardCharsets.UTF_8);
            OutputStream os = conn.getOutputStream();
            os.write(outputInBytes);
            os.close();
            // 获取服务器响应代码
            int responseCode = conn.getResponseCode();
            //System.out.println("POST Response Code :: " + responseCode);
            if (responseCode == HttpURLConnection.HTTP_OK) { // 成功响应
                // 读取服务器响应内容
                java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
                String ret = response.toString();
                if (ret.startsWith("\"")) {
                    ret = ret.substring(1, ret.length() - 1);
                }
                // 打印结果
                //System.out.println("[runPython_raw]ret->" + ret);
                return ret;
            }
        } catch (Exception e) {
            System.out.println("[runPython_raw] error=" + e.getMessage());
        }
        return "execute error";
    }

    public static void main(String[] args) {
        String originalString = "Hello, World!";
        String encodedString = Base64.getEncoder().encodeToString(originalString.getBytes());

        // 编码3次
        String encodedTwice = Base64.getEncoder().encodeToString(encodedString.getBytes());
        String encodedThrice = Base64.getEncoder().encodeToString(encodedTwice.getBytes());

        // 输出结果
        System.out.println("Original String: " + originalString);
        System.out.println("Encoded Once: " + encodedString);
        System.out.println("Encoded Twice: " + encodedTwice);
        System.out.println("Encoded Thrice: " + encodedThrice);

        // 验证结果是否相同
        if (encodedString.equals(encodedTwice) && encodedTwice.equals(encodedThrice)) {
            System.out.println("Encoding the string 3 times results in the same output.");
        } else {
            System.out.println("Encoding the string 3 times does not result in the same output.");
        }
//        String md5 = ChatgptFunction.calculateMD5("/home/softrobot/llm/qwne05");
//        System.out.println(md5); // 输出: 你好
//        // 假设我们有一个包含自定义Unicode转义序列的字符串
//        String unicodeEscape = "U&'\\6587\\672c1\\ff1a\\7cd6\\5c3f\\75c5\\817f\\9ebb\\6728\\600e\\4e48\\529e\\ff1f\\\\n\\6587\\672c2\\ff1a\\7cd6\\5c3f\\75c5\\600e\\6837\\63a7\\5236\\751f\\6d3b\\65b9\\5f0f'";
//        System.out.println(unicodeEscape); // 输出: 你好
//        //unicodeEscape = "U&'\\4f60\\597dhello'";
//        System.out.println(unicodeEscape); // 输出: 你好
//
//        // 去除前缀U&，并处理剩余的Unicode转义序列
//        String chineseString = decodeUnicode(unicodeEscape);
//
//        // 输出结果
//        System.out.println(chineseString); // 输出: 你好
    }
}
